/*
 * mediaplayer.cpp
 */

//#define LOG_NDEBUG 0
#define TAG "decoder"

#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>

extern "C" {

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/log.h"
#include "global.h"

} // end of extern C

#include "decoder.h"

#define FPS_DEBUGGING false

static Decoder* sPlayer;
static int stopStatus;

Decoder::Decoder(JNIEnv* env) {
	env->GetJavaVM(&gs_jvm); //���浽ȫ�ֱ�����JVM
	jniEnv = env;
	mMovieFile = NULL;
	mURL = NULL;
	mURL1 = NULL;
	mURL2 = NULL;
	mURL3 = NULL;
	mURL4 = NULL;
	mURL5 = NULL;
	isUrlNotNull = true;
	curUrlID = 0;
	mCookie = NULL;
	mDuration = -1;
	mCurrentPosition = -1;
	mSeekPosition = -1;
	mCurrentState = MEDIA_PLAYER_IDLE1;
	mPrepareSync = false;
	mLoop = false;
	pthread_mutex_init(&mLock, NULL);
	mLeftVolume = mRightVolume = 1.0;
	mVideoWidth = mVideoHeight = 0;
	sPlayer = this;
	mVideoParamCount = 0;
	mScopeOn = false;
//	mTreaterVideo = NULL;
	mVideoStreamIndex = -1;
	mVideoFrame = NULL;
	record = NULL;
	avcodec_register_all();
	av_register_all();
	avformat_network_init();

	//gmq add begin

	revokeFrameDataMethod = NULL;

	isIFrame = false;
	isFrameIncompleted = false;
	stopStatus = 0;

	//gmq add end
	__android_log_print(ANDROID_LOG_INFO, TAG, "init...");
}

Decoder::~Decoder() {

	if (mURL != NULL) {
		delete[] mURL;
		mURL = NULL;
	}
}

void Decoder::setParamCount(int videoParam) {
	if (videoParam != mVideoParamCount) {
		mVideoParamCount = videoParam;
	}
}

bool Decoder::prepareVideo() {
	__android_log_print(ANDROID_LOG_INFO, TAG, "prepareVideo");
	// Find the first video stream
	mVideoStreamIndex = -1;
	for (int i = 0; i < mMovieFile->nb_streams; i++) {
		if (mMovieFile->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
			mVideoStreamIndex = i;
			break;
		}
	}
	__android_log_print(ANDROID_LOG_INFO, TAG, "prepareVideo 1");
	if (mVideoStreamIndex == -1) {
		return false;
	}
	__android_log_print(ANDROID_LOG_INFO, TAG, "prepareVideo 2");

	AVStream* stream = mMovieFile->streams[mVideoStreamIndex];
	// Get a pointer to the codec context for the video stream
	AVCodecContext* codec_ctx = stream->codec;
	AVCodec* codec = avcodec_find_decoder(codec_ctx->codec_id);

	if (codec == NULL) {
		return false;
	}

	codec_ctx->thread_count = 2;

	__android_log_print(ANDROID_LOG_INFO, TAG, "prepareVideo 3");
	// Open codec
	if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
		return false;
	}
	__android_log_print(ANDROID_LOG_INFO, TAG, "prepareVideo 3.1");
	mVideoWidth = codec_ctx->width;
	mVideoHeight = codec_ctx->height;
	mDuration = mMovieFile->duration;
	__android_log_print(ANDROID_LOG_INFO, TAG, "prepareVideo 3.2 w=%d h=%d",
			mVideoWidth, mVideoHeight);
	if (stream->codec->pix_fmt == NULL) {
		__android_log_print(ANDROID_LOG_INFO, TAG,
				"prepareVideo 3.3 stream codec null");
	}
	if (mVideoWidth == 0) {
		return false;
	}
	mVideoSwsContext = sws_getContext(mVideoWidth, mVideoHeight,
			stream->codec->pix_fmt, mVideoWidth, mVideoHeight, PIX_FMT_RGB565, //PIX_FMT_RGB565,gmq switch 565 to 24
			SWS_BILINEAR, NULL, NULL, NULL);
	__android_log_print(ANDROID_LOG_INFO, TAG, "prepareVideo 4");
	if (mVideoSwsContext == NULL) {
		return false;
	}

	avStreamFPSTimeBase(stream, 0.04, &mVideoFPS, &mVideoTimeBase);
	int avpictureValid = avpicture_alloc(&mVideoPicture, PIX_FMT_RGB565, //PIX_FMT_RGB565,gmq switch 565 to 24
			mVideoWidth, mVideoHeight);
	if (avpictureValid < 0) {
		return false;
	}
	__android_log_print(ANDROID_LOG_INFO, TAG, "prepareVideo 5");
	return true;
}

void Decoder::closeVideo() {

	if (mVideoSwsContext != NULL) {
		sws_freeContext(mVideoSwsContext);
		mVideoSwsContext = NULL;
	}

	avpicture_free(&mVideoPicture);

	//av_free(mVideoFrame);
}

bool Decoder::prepare() {
	status_t ret;
	mCurrentState = MEDIA_PLAYER_PREPARING1;
	__android_log_print(ANDROID_LOG_ERROR, TAG, "prepare 1");
	if (!(ret = prepareVideo())) {
		__android_log_print(ANDROID_LOG_ERROR, TAG, "prepare video wrong");
		mCurrentState = MEDIA_PLAYER_STATE_ERROR1;
		return false;
	}
	__android_log_print(ANDROID_LOG_ERROR, TAG, "prepare 2");
//	prepareAudio();
//	if ((ret = prepareAudio()) != NO_ERROR) {
//		mCurrentState = MEDIA_PLAYER_STATE_ERROR;
//		return ret;
//	}
	mCurrentState = MEDIA_PLAYER_PREPARED1;
	return true;
}

char* Decoder::getUrl(int id) {
	switch (id) {
	case 0:
		return mURL;
		break;
	case 1:
		return mURL1;
		break;
	case 2:
		return mURL2;
		break;
	case 3:
		return mURL3;
		break;
	case 4:
		return mURL4;
		break;
	case 5:
		return mURL5;
		break;
	}
}

bool Decoder::connect() {
	if (mURL == NULL)
		return false;
	char* url = getUrl(curUrlID);
	av_register_all();
	__android_log_print(ANDROID_LOG_ERROR, TAG, "begin connect");
	AVFormatContext* formatCtx = avformat_alloc_context(); //avformat_free_context()
	__android_log_print(ANDROID_LOG_INFO, TAG, "setDataSource(%s)", mURL);
	__android_log_print(ANDROID_LOG_ERROR, TAG, "after alloc context");

	int result = avformat_open_input(&formatCtx, url, NULL, NULL);
	__android_log_print(ANDROID_LOG_ERROR, TAG, "after open input result=%d",
			result);
//	int result = avformat_open_input(&formatCtx, mRtspURL, NULL, NULL);	//&opts);

	if (result != 0) {
		char buf[1024];
		av_strerror(result, buf, 1024);
		__android_log_print(ANDROID_LOG_INFO, TAG, "Couldn't open file %d(%s)",
				result, buf);
		if (formatCtx) {
			avformat_free_context(formatCtx);
		}

		return false;
	}
	__android_log_print(ANDROID_LOG_ERROR, TAG, "connect 1");

	// Retrieve stream information
	int r = avformat_find_stream_info(formatCtx, NULL);
	if (r < 0) {
		avformat_close_input(&formatCtx);
		__android_log_print(ANDROID_LOG_INFO, TAG, "find stream fail...");
		return false;
	}
	if (stopStatus == 1) {
		return false;
	}

	__android_log_print(ANDROID_LOG_ERROR, TAG, "connect 2+ r=%d", r);

//	av_dump_format(formatCtx, 0, mRtspURL, false);
	mCurrentState = MEDIA_PLAYER_INITIALIZED1;
	mMovieFile = formatCtx;
	__android_log_print(ANDROID_LOG_ERROR, TAG, "connect 3");
	return true;
}

void Decoder::setDataSource(char *url) {
	if (mURL != NULL) {
		delete[] mURL;
		mURL = NULL;
	}
	mURL = url;
}

void Decoder::setDataUrl(char *url, char *url1, char *url2, char *url3,
		char *url4, char *url5, int count) {
	mURL = url;
	mURL1 = url1;
	mURL2 = url2;
	mURL3 = url3;
	mURL4 = url4;
	mURL5 = url5;
	urlCount = count;
}

bool Decoder::suspend() {
	__android_log_print(ANDROID_LOG_INFO, TAG, "suspend");

	mCurrentState = MEDIA_PLAYER_STOPPED1;

//	if (mTreaterVideo != NULL) {
//		mTreaterVideo->stop();
//	}

	if (pthread_join(mPlayerThread, NULL) != 0) {
		__android_log_print(ANDROID_LOG_ERROR, TAG,
				"Couldn't cancel player thread");
	}

	// Close the codec

//	delete mTreaterVideo;

//	mTreaterVideo = NULL;

	// Close the video file
	if (mMovieFile != NULL) {
		avformat_close_input(&mMovieFile);
		avformat_free_context(mMovieFile);
		mMovieFile = NULL;
	}
	__android_log_print(ANDROID_LOG_ERROR, TAG, "suspended");

	return true;
}

bool Decoder::resume() {
	//pthread_mutex_lock(&mLock);
	mCurrentState = MEDIA_PLAYER_STARTED1;
	//pthread_mutex_unlock(&mLock);
	return true;
}

bool Decoder::StartScope(const char* scopeFile) {
	__android_log_print(ANDROID_LOG_ERROR, TAG, "start write");
	if (!mScopeOn) {
		__android_log_print(ANDROID_LOG_ERROR, TAG, "start write1");
		bool result = false;
		record = new Writer();
		result = record->StartScopeEx(scopeFile, 1280, 720);
		record->startAsync();

		__android_log_print(ANDROID_LOG_ERROR, TAG, "start write2");
		if (result) {
			__android_log_print(ANDROID_LOG_ERROR, TAG, "start write3");
			findKeyFrame = false;
			mScopeOn = true;
			__android_log_print(ANDROID_LOG_ERROR, TAG,
					"Start scope in mediaplayer--after get startscopeEx");
			return true;
		}
	}
	return false;
}

bool Decoder::EndScope() {
	if (mScopeOn) {
		record->StopScope();
		mScopeOn = false;
		if (record != NULL) {
			record->stop();

			// Close the codec
			delete record;
			record = NULL;

			__android_log_print(ANDROID_LOG_INFO, TAG, "end scope thread...");
		}
		return true;
	}
	return false;
}

void Decoder::decodeMovie(void* ptr) {
	//�̹߳���jnienv
	__android_log_print(ANDROID_LOG_ERROR, TAG, "enter decode movie");
	gs_jvm->AttachCurrentThread(&DataRecvThreadJniEnv, NULL);
	revokeMethod = DataRecvThreadJniEnv->GetMethodID(revokeClass, "onComplete",
			"()V");

	mCurrentState = MEDIA_PLAYER_BEGIN_THREAD1;
	while (isUrlNotNull) {
		__android_log_print(ANDROID_LOG_INFO, TAG, "connect again");
		if (connect()) {
			if (prepare()) {
				__android_log_print(ANDROID_LOG_ERROR, TAG, "enter connect");
				if (revokeInstance == NULL) {
					__android_log_print(ANDROID_LOG_ERROR, TAG,
							"instance null");
				}
				__android_log_print(ANDROID_LOG_ERROR, TAG, "enter connect 1");
				if (revokeMethod == NULL) {
					__android_log_print(ANDROID_LOG_ERROR, TAG, "method null");
				}
				__android_log_print(ANDROID_LOG_ERROR, TAG,
						"enter connect---after revoke");
				AVCodecContext* videoCodec;
				AVStream* stream_video;
				AVPacket* pPacket = NULL;

				stream_video = mMovieFile->streams[mVideoStreamIndex];
				videoCodec = stream_video->codec;

				mCurrentState = MEDIA_PLAYER_STARTED1;
				__android_log_print(ANDROID_LOG_INFO, TAG, "playing %ix%i",
						mVideoWidth, mVideoHeight);

				unsigned char* mVideoParam = NULL;
				if (mVideoParamCount > 0) {
					mVideoParam = new unsigned char[mVideoParamCount];
				}

				bool firstIn = true;
				bool isBeginScope = false;
				int completed = 0;
				Operation operation;
				__android_log_print(ANDROID_LOG_INFO, TAG,
						"prepare to start main thread...");

				while (mCurrentState != MEDIA_PLAYER_DECODED1
						&& mCurrentState != MEDIA_PLAYER_STOPPED1
						&& mCurrentState != MEDIA_PLAYER_STATE_ERROR1) {
					__android_log_print(ANDROID_LOG_ERROR, TAG, "read frame");
					operation = FrameOperation_None;
					completed = 0;
					pPacket = new AVPacket();
					int readFrame = av_read_frame(mMovieFile, pPacket);
					if (readFrame < 0) {
						__android_log_print(ANDROID_LOG_INFO, TAG,
								"read frame fail...---curId=%d---count=%d",
								curUrlID, urlCount);
						av_free_packet(pPacket);
						delete pPacket;
						if (curUrlID < urlCount - 1) {
							curUrlID++;
						} else {
							isUrlNotNull = false;
						}
						usleep(2000);
						break;
					}

					if (pPacket->stream_index == mVideoStreamIndex) {
						int pktSize = pPacket->size;
						while (pktSize > 0) {
							mVideoFrame = av_frame_alloc();
							int len = avcodec_decode_video2(videoCodec,
									mVideoFrame, &completed, pPacket);

							if (len <= 0) {
								__android_log_print(ANDROID_LOG_ERROR, TAG,
										"decode frame < 0");
								av_frame_free(&mVideoFrame);
								mVideoFrame = NULL;
								break;
							}

							if (mScopeOn) {
								__android_log_print(ANDROID_LOG_ERROR, TAG,
										"read frame and write to scope");
								sPlayer->record->WriteVideoFrame1(mVideoFrame,
										FrameOperation_Scope); //pass the packet to record queue
							} else {
								av_free_packet(pPacket); //free it if we don't need it anymore
								delete pPacket;
							}
							pktSize -= len;
						}

					}
					usleep(20000);
				}

				releaseResource();
			} else {
//			DataRecvThreadJniEnv->CallVoidMethod(revokeInstance, revokeMethod,
//					0, -1, -1);
				if (curUrlID < urlCount - 1) {
					curUrlID++;
				} else {
					isUrlNotNull = false;
				}
				mCurrentState = MEDIA_PLAYER_STOPPED1;
			}

		} else {
//		DataRecvThreadJniEnv->CallVoidMethod(revokeInstance, revokeMethod, 0,
//				-1, -1);
			if (curUrlID < urlCount - 1) {
				curUrlID++;
			} else {
				isUrlNotNull = false;
			}
			mCurrentState = MEDIA_PLAYER_STOPPED1;
		}
	}
	DataRecvThreadJniEnv->CallVoidMethod(revokeInstance, revokeMethod);
	EndScope();
	gs_jvm->DetachCurrentThread();
	DataRecvThreadJniEnv = NULL;
}

void Decoder::releaseResource() {
	__android_log_print(ANDROID_LOG_INFO, TAG, "prepare to end main thread...");

	if (mCurrentState == MEDIA_PLAYER_STATE_ERROR1) {
		__android_log_print(ANDROID_LOG_INFO, TAG, "playing err");
	}
	mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE1;
	__android_log_print(ANDROID_LOG_INFO, TAG, "end of playing");
}

void Decoder::avStreamFPSTimeBase(AVStream *st, float defaultTimeBase,
		float *pFPS, float *pTimeBase) {
	float fps, timebase;

	if (st->time_base.den && st->time_base.num)
		timebase = av_q2d(st->time_base);
	else if (st->codec->time_base.den && st->codec->time_base.num)
		timebase = av_q2d(st->codec->time_base);
	else
		timebase = defaultTimeBase;

	if (st->codec->ticks_per_frame != 1) {
		__android_log_print(ANDROID_LOG_INFO, TAG,
				"WARNING: st.codec.ticks_per_frame=%d",
				st->codec->ticks_per_frame);
		//timebase *= st->codec->ticks_per_frame;
	}

	if (st->avg_frame_rate.den && st->avg_frame_rate.num)
		fps = av_q2d(st->avg_frame_rate);
	else if (st->r_frame_rate.den && st->r_frame_rate.num)
		fps = av_q2d(st->r_frame_rate);
	else
		fps = 1.0 / timebase;

	if (pFPS)
		*pFPS = fps;
	if (pTimeBase)
		*pTimeBase = timebase;
	__android_log_print(ANDROID_LOG_ERROR, TAG, "timebase=%f----fps=%f--",
			timebase, fps);
}

void* Decoder::startPlayer(void* ptr) {
	//�̹߳���jnienv
	//gs_jvm->AttachCurrentThread(&mainThreadJniEnv, NULL);

	__android_log_print(ANDROID_LOG_INFO, TAG, "starting main player thread");
	sPlayer->decodeMovie(ptr);

	//gs_jvm->DetachCurrentThread();
	//mainThreadJniEnv = NULL;
	__android_log_print(ANDROID_LOG_INFO, TAG, "ending main player thread");
}

bool Decoder::start() {
	if (mCurrentState != MEDIA_PLAYER_IDLE1
			&& mCurrentState != MEDIA_PLAYER_STOPPED1) {
		return false;
	}

	pthread_create(&mPlayerThread, NULL, startPlayer, NULL);

	return true;
}

void Decoder::setStop(int isStop) {
	__android_log_print(ANDROID_LOG_ERROR, TAG, "isStop=%d", isStop);
	stopStatus = isStop;
}

bool Decoder::stop() {
	//pthread_mutex_lock(&mLock);
	if (isPlaying()) {
		mCurrentState = MEDIA_PLAYER_STOPPED1;

		//�ȹرղɼ��̣߳��Է�ֹ�ɼ��̼߳����������߳���������
		if (pthread_join(mPlayerThread, NULL) != 0) {
			__android_log_print(ANDROID_LOG_ERROR, TAG,
					"Couldn't cancel player thread");
		}

		__android_log_print(ANDROID_LOG_ERROR, TAG, "before close video");
		closeVideo();
		__android_log_print(ANDROID_LOG_ERROR, TAG, "after close video");
		// Close the video file
		if (mMovieFile != NULL) {
			avformat_close_input(&mMovieFile);
			avformat_free_context(mMovieFile);
			mMovieFile = NULL;
			__android_log_print(ANDROID_LOG_ERROR, TAG,
					"after close mMOvieFile");
		}

//		jmethodID revokeStopMethod = jniEnv->GetMethodID(revokeClass,
//				"OnConnectionStop", "()V");
//		jniEnv->CallVoidMethod(revokeInstance, revokeStopMethod);
//
//		__android_log_print(ANDROID_LOG_ERROR, TAG,"stop player complete");
		return true;
	} else {
		return false;
	}

	//pthread_mutex_unlock(&mLock);

}

bool Decoder::pause() {
	//pthread_mutex_lock(&mLock);
	mCurrentState = MEDIA_PLAYER_PAUSED1;
	//pthread_mutex_unlock(&mLock);
	return true;
}

bool Decoder::isPlaying() {
	return mCurrentState == MEDIA_PLAYER_STARTED1
			|| mCurrentState == MEDIA_PLAYER_DECODED1;
}

bool Decoder::isBegin() {
	return mCurrentState == MEDIA_PLAYER_BEGIN_THREAD1
			|| mCurrentState == MEDIA_PLAYER_INITIALIZED1
			|| mCurrentState == MEDIA_PLAYER_PREPARING1
			|| mCurrentState == MEDIA_PLAYER_PREPARED1;
}

bool Decoder::getVideoWidth(int *w) {
	if (mCurrentState < MEDIA_PLAYER_PREPARED1) {
		return false;
	}
	*w = mVideoWidth;
	return true;
}

bool Decoder::getVideoHeight(int *h) {
	if (mCurrentState < MEDIA_PLAYER_PREPARED1) {
		return false;
	}
	*h = mVideoHeight;
	return true;
}

bool Decoder::getDuration(int *msec) {
	if (mCurrentState < MEDIA_PLAYER_PREPARED1) {
		return false;
	}
	*msec = mDuration / 1000;
	return true;
}
